module axis_player(
  input           CLK,
  input           RST,

  input           RP_EN,
  input   [3:0]   RP_WE,
  input   [2:0]   RP_A,
  input   [31:0]  RP_DI,
  output reg [31:0]  RP_DO = 0,
  output reg      RP_RDY,

//  output  [47:0]  TAG,
  
  output          M_TVALID,
  input           M_TREADY,
  output  [127:0] M_TDATA,
  output  [3:0]   M_TSTRB,
  output  [47:0]  M_TUSER,
  output          M_TLAST,

  output  [0:0]   M_ARID,
  output  [31:0]  M_ARADDR,
  output  [7:0]   M_ARLEN,
  output  [2:0]   M_ARSIZE,
  output  [1:0]   M_ARBURST,
  output          M_ARVALID,
  input           M_ARREADY,
  input   [0:0]   M_RID,
  input   [127:0] M_RDATA,
  input   [1:0]   M_RRESP,
  input           M_RLAST,
  input           M_RVALID,
  output          M_RREADY
  );
    
  reg [2:0] rState, sState;
  reg EnablePlaying;
  reg [31:0] DescriptorAddr;
  reg [31:0] PacketAddr;
  reg [15:0] PacketLength;
  reg [31:0] FirstDescriptorAddr;
  reg [7:0] PacketARLen;
  reg [47:0] User;
  reg [31:0] PacketCount;
  
  localparam
    IDLE = 0,
    DESCRIPTOR_AR = 1,
    DESCRIPTOR_R = 2,
    BOUNDARY_CHECK = 3,
    PACKET_AR = 4,
    PACKET_R = 5,
    DONE = 6;

  always @(posedge CLK)
  begin
    if (RST)
      rState <= IDLE;
    else
      rState <= sState;
  end
  
  always @*
  begin
    sState <= rState;
    case (rState)
      IDLE:
        if (EnablePlaying)
        begin
          if (|DescriptorAddr)
            sState <= DESCRIPTOR_AR;
          else
            sState <= DONE;
        end
      DESCRIPTOR_AR:
        if (M_ARREADY)
          sState <= DESCRIPTOR_R;
      DESCRIPTOR_R:
        if (M_RVALID)
          sState <= BOUNDARY_CHECK;
      BOUNDARY_CHECK:
        if (|PacketLength)
          sState <= PACKET_AR;
        else if ((|DescriptorAddr) && EnablePlaying)
          sState <= DESCRIPTOR_AR;
        else
          sState <= DONE;
      PACKET_AR:
        if (M_ARREADY)
          sState <= PACKET_R;
      PACKET_R:
        if (M_RVALID && M_RREADY && M_RLAST)
          sState <= BOUNDARY_CHECK;
      DONE:
        if (!EnablePlaying)
          sState <= IDLE;
    endcase
  end

  always @(posedge CLK)
  begin
    if (rState == IDLE)
      DescriptorAddr <= FirstDescriptorAddr;
    else if ((rState == DESCRIPTOR_R) && M_RVALID)
      DescriptorAddr <= M_RDATA[31:0];//[63:32];
  end
  
  always @(posedge CLK)
  begin
    if ((rState == DESCRIPTOR_R) && M_RVALID)
      PacketAddr <= M_RDATA[63:32];//[31:0];
    else if ((rState == PACKET_R) && M_RVALID && M_RREADY)
      PacketAddr <= PacketAddr + 16; 
  end
  
  always @(posedge CLK)
  begin
    if ((rState == DESCRIPTOR_R) && M_RVALID)
      PacketLength <= M_RDATA[79:64];
    else if ((rState == PACKET_R) && M_RVALID && M_RREADY)
    begin
      if (PacketLength > 15)
        PacketLength <= PacketLength - 16;
      else
        PacketLength <= 0;
    end
  end
  
  always @(posedge CLK)
  begin
    if ((rState == DESCRIPTOR_R) && M_RVALID)
      User <= M_RDATA[127:80];
  end

  wire [15:0] sPacketLengthMinusOne = PacketLength - 1;
  wire [15:0] sRemainingPageMinusOne = 16'h0FFF - PacketAddr[11:0];
  always @(posedge CLK)
  begin
    if (rState == BOUNDARY_CHECK)
    begin
      if (PacketLength < (16'h1000 - PacketAddr[11:0]))
      begin
        PacketARLen <= sPacketLengthMinusOne[11:4];
      end
      else
        PacketARLen <= sRemainingPageMinusOne[11:4];
    end
  end
  
  always @(posedge CLK)
  begin
    if ((rState == IDLE) && EnablePlaying)
      PacketCount <= 00;
    else if (M_TVALID && M_TREADY && M_TLAST)
      PacketCount <= PacketCount + 1;
  end

  always @(posedge CLK)
  begin
    if (RST || RP_RDY)
      RP_RDY <= 1'b0;
    else if(RP_EN)
      RP_RDY <= 1'b1;
  end

  always @(posedge CLK)
  begin
    if (RST)
    begin
      EnablePlaying <= 1'b0;
    end
    else if (RP_EN)
    begin
      case (RP_A)
        3'h0: 
          begin
            RP_DO <= {13'h0, rState, 15'h0, EnablePlaying};
            if (RP_WE[0]) EnablePlaying <= RP_DI[0];
          end
        3'h1:
          begin
            RP_DO <= FirstDescriptorAddr;
            if (RP_WE[0]) FirstDescriptorAddr[7:0] <= {RP_DI[7:4], 4'b0};
            if (RP_WE[1]) FirstDescriptorAddr[15:8] <= RP_DI[15:8];
            if (RP_WE[2]) FirstDescriptorAddr[23:16] <= RP_DI[23:16];
            if (RP_WE[3]) FirstDescriptorAddr[31:24] <= RP_DI[31:24];
          end
        3'h2: RP_DO <= DescriptorAddr;
        3'h5: RP_DO <= PacketCount;
        default: RP_DO <= 32'h0;
      endcase
    end
  end

  assign M_TVALID = (rState == PACKET_R) && M_RVALID;
  assign M_TDATA = M_RDATA;
  assign M_TSTRB =  (PacketLength > 12) ? 4'hF :
                    (PacketLength > 8) ? 4'h7 :
                    (PacketLength > 4) ? 4'h3 :
                    (PacketLength > 0) ? 4'h1 : 4'h0;
  assign M_TUSER = User;
  assign M_TLAST = (PacketLength <= 16);
  assign M_ARID = 1'b0;//(rState == DESCRIPTOR_AR);
  assign M_ARADDR = (rState == DESCRIPTOR_AR) ? DescriptorAddr :
                    (rState == PACKET_AR) ? PacketAddr :
                    32'b0;
  assign M_ARLEN = (rState == DESCRIPTOR_AR) ? 8'b0 :
                   (rState == PACKET_AR) ? PacketARLen :
                   8'b0;
  assign M_ARSIZE = 3'b100;
  assign M_ARBURST = 2'b01;
  assign M_ARVALID = (rState == DESCRIPTOR_AR) || (rState == PACKET_AR);
  assign M_RREADY = (rState == DESCRIPTOR_R)|| ((rState == PACKET_R) && M_TREADY);
  
  
endmodule
